#include "WindowAdds.h"

HWINEVENTHOOK WindowAdds::hWinEventHook=0;
set<WindowAdds*> WindowAdds::instances;


HINSTANCE WindowAdds::hInstance;
HHOOK WindowAdds::hMouseHook;
bool WindowAdds::mouseHookActive = false;

WindowAdds::WindowAdds(HINSTANCE hInstance)
{
	if (instances.empty())
	{
		WindowAdds::hInstance = hInstance;
		hWinEventHook=SetWinEventHook(EVENT_SYSTEM_FOREGROUND, EVENT_OBJECT_LOCATIONCHANGE, 0, WinEventProc, 0, 0, 0);
		hMouseHook = SetWindowsHookEx(WH_MOUSE_LL, MouseHookProc, hInstance, 0);

	}

	menuHeight = GetSystemMetrics(SM_CYMENU);
	captionHeight = GetSystemMetrics(SM_CYCAPTION);
	frameHeight = GetSystemMetrics(SM_CYFRAME);
	smallCaptionHeight = GetSystemMetrics(SM_CYSMCAPTION);
	minHeight = captionHeight + frameHeight * 2;
	minSmallHeight = smallCaptionHeight + frameHeight * 2;

	instances.insert(this);
}

WindowAdds::~WindowAdds()
{
	ResetReducedWindows();

	instances.erase(this);

	if (instances.empty())
	{
		UnhookWinEvent(hWinEventHook);
		
		UnhookWindowsHookEx(hMouseHook);
	}
}

void WindowAdds::Add(HWND hWnd)
{
	if (Exists(hWnd))
		return;

	ReducedData data={0};
	windowsForReducing[hWnd] = data;
}

void WindowAdds::Remove(HWND hWnd)
{
	if (!Exists(hWnd))
		return; 
	windowsForReducing.erase(hWnd);
}

bool WindowAdds::Exists(HWND hWnd)
{
	return (windowsForReducing.find(hWnd) != windowsForReducing.end());
}


void WindowAdds::ResetReducedWindows()
{
	map<HWND,ReducedData>::iterator iter;
	for (iter = windowsForReducing.begin();iter!=windowsForReducing.end();iter++)
	{
		if (iter->second.Size.Height)
		{
			RestoreReducedWindow(iter->first);
		}
	}
}

void WindowAdds::ReduceWindowsExceptOne(HWND hWnd, HWND secondHWnd)
{
	map<HWND,ReducedData>::iterator iter;
	for (iter = windowsForReducing.begin();iter!=windowsForReducing.end();iter++)
	{
		if (iter->second.Size.Height==0
			&& GetParent(hWnd) != iter->first
			&& GetParent(secondHWnd) != iter->first
			&& GetWindow(hWnd, GW_OWNER) != iter->first
			&& GetWindow(secondHWnd, GW_OWNER) != iter->first
			&& hWnd != iter->first
			&& secondHWnd != iter->first)
			ReduceWindow(iter->first);
	}
}

void WindowAdds::RestoreReducedWindow(HWND hWnd)
{
	HWND parent = GetParent(hWnd);
	HWND owner = GetWindow(hWnd, GW_OWNER);

	if (Exists(hWnd) && IsWindow(hWnd) && (GetWindowLongPtr(hWnd,GWL_STYLE) & WS_CAPTION) == WS_CAPTION && windowsForReducing[hWnd].Size.Height)
	{
		if (!IsIconic(hWnd))
		{
			SetWindowPos(hWnd,0,0,0,windowsForReducing[hWnd].Size.Width,windowsForReducing[hWnd].Size.Height,SWP_NOMOVE|SWP_NOACTIVATE|SWP_NOSENDCHANGING);
			InvalidateRgn(hWnd, 0, TRUE);
		}
		else
		{
			WINDOWPLACEMENT wp;
			wp.length = sizeof(WINDOWPLACEMENT);
			GetWindowPlacement(hWnd, &wp);
			wp.rcNormalPosition.right = wp.rcNormalPosition.left + windowsForReducing[hWnd].Size.Width;
			wp.rcNormalPosition.bottom = wp.rcNormalPosition.top + windowsForReducing[hWnd].Size.Height;
			SetWindowPlacement(hWnd, &wp);
		}

		windowsForReducing[hWnd].Size.Height=0;
	}
	else if (Exists(parent) && IsWindow(parent) && (GetWindowLongPtr(parent,GWL_STYLE) & WS_CAPTION) == WS_CAPTION && windowsForReducing[parent].Size.Height)
	{
		if (!IsIconic(parent))
		{
			SetWindowPos(parent,0,0,0,windowsForReducing[parent].Size.Width,windowsForReducing[parent].Size.Height,SWP_NOMOVE|SWP_NOACTIVATE|SWP_NOSENDCHANGING);
			InvalidateRgn(parent, 0, TRUE);
		}
		else
		{
			WINDOWPLACEMENT wp;
			wp.length = sizeof(WINDOWPLACEMENT);
			GetWindowPlacement(parent, &wp);
			wp.rcNormalPosition.right = wp.rcNormalPosition.left + windowsForReducing[parent].Size.Width;
			wp.rcNormalPosition.bottom = wp.rcNormalPosition.top + windowsForReducing[parent].Size.Height;
			SetWindowPlacement(parent, &wp);
		}

		windowsForReducing[parent].Size.Height=0;
	}
	else if (Exists(owner) && IsWindow(owner) && (GetWindowLongPtr(owner,GWL_STYLE) & WS_CAPTION) == WS_CAPTION && windowsForReducing[owner].Size.Height)
	{
		if (!IsIconic(owner))
		{
			SetWindowPos(owner,0,0,0,windowsForReducing[owner].Size.Width,windowsForReducing[owner].Size.Height,SWP_NOMOVE|SWP_NOACTIVATE|SWP_NOSENDCHANGING);
			InvalidateRgn(owner, 0, TRUE);
		}
		else
		{
			WINDOWPLACEMENT wp;
			wp.length = sizeof(WINDOWPLACEMENT);
			GetWindowPlacement(owner, &wp);
			wp.rcNormalPosition.right = wp.rcNormalPosition.left + windowsForReducing[owner].Size.Width;
			wp.rcNormalPosition.bottom = wp.rcNormalPosition.top + windowsForReducing[owner].Size.Height;
			SetWindowPlacement(owner, &wp);
		}

		windowsForReducing[owner].Size.Height=0;
	}
}

void WindowAdds::ReduceWindow(HWND hWnd)
{
	if (IsWindow(hWnd) && (GetWindowLongPtr(hWnd,GWL_STYLE) & WS_CAPTION) == WS_CAPTION && Exists(hWnd) && !windowsForReducing[hWnd].Size.Height)
	{
		RECT windowRect = Utilities::CalcWindowRect(hWnd);

		RECT clientRect;
		GetClientRect(hWnd,&clientRect);

		TCHAR className[255];
		memset(className,0,sizeof(className));
		GetClassName(hWnd,className,sizeof(className));

		windowsForReducing[hWnd].Size.Height=windowRect.bottom-windowRect.top;
		windowsForReducing[hWnd].Size.Width=windowRect.right-windowRect.left;

		int height=(windowRect.bottom-windowRect.top)-(clientRect.bottom-clientRect.top);

		if (GetMenu(hWnd))
			height-=menuHeight;

		if (!_tcscmp(className,TEXT("CabinetWClass")))
		{
			HWND workerW = FindWindowEx(hWnd,0,TEXT("WorkerW"),0);
			if (workerW)
			{
				RECT workerWRect;
				GetWindowRect(workerW,&workerWRect);
				height+=(workerWRect.bottom-workerWRect.top);
			}
		}

		if ((GetWindowLongPtr(hWnd,GWL_EXSTYLE) & WS_EX_TOOLWINDOW) == WS_EX_TOOLWINDOW)
		{
			if (height < minSmallHeight)
				height = minSmallHeight;
		}
		else
		{
			if (height < minHeight)
				height = minHeight;
		}

		SetWindowPos(hWnd,0,0,0,windowRect.right-windowRect.left,height,SWP_NOMOVE|SWP_NOACTIVATE|SWP_NOSENDCHANGING);
	}
}


VOID CALLBACK WindowAdds::WinEventProc(HWINEVENTHOOK hWinEventHook, DWORD dwEvent, HWND hWnd, LONG idObject, LONG idChild, DWORD idEventThread, DWORD dwmsEventTime)
{
	if ((dwEvent == EVENT_SYSTEM_FOREGROUND || dwEvent == EVENT_SYSTEM_MINIMIZEEND) && idObject == OBJID_WINDOW)
	{
		if (!IsIconic(hWnd))
		{
			set<WindowAdds*>::iterator iter;
			for(iter=instances.begin(); iter!=instances.end(); iter++)
			{
				(*iter)->ReduceWindowsExceptOne(Utilities::TopLevelWindow(hWnd));
				(*iter)->RestoreReducedWindow(Utilities::TopLevelWindow(hWnd));
			}
		}
	}
	else if (dwEvent == EVENT_OBJECT_DESTROY && idObject == OBJID_WINDOW)
	{
		set<WindowAdds*>::iterator iter;
		for(iter=instances.begin(); iter!=instances.end(); iter++)
		{
			if ((*iter)->Exists(hWnd))
				(*iter)->Remove(hWnd);
		}

	}
	else if (dwEvent == EVENT_OBJECT_LOCATIONCHANGE && idObject == OBJID_CURSOR)
	{	
		HWND h = Utilities::TopLevelWindowFromMousePosition();
		
		set<WindowAdds*>::iterator iter;
		for(iter=instances.begin(); iter!=instances.end(); iter++)
		{
			(*iter)->RestoreReducedWindow(h);
			(*iter)->ReduceWindowsExceptOne(h, GetForegroundWindow());
		}





		//----mouse hook backup----
		static int mouseMoves = 0;

		if (mouseMoves > 15)
		{
			if (!mouseHookActive)
			{
				UnhookWindowsHookEx(hMouseHook);
				hMouseHook = SetWindowsHookEx(WH_MOUSE_LL, MouseHookProc, hInstance, 0);
			}

			mouseHookActive = false;
			mouseMoves = 0;
		}

		mouseMoves++;
	}
}




LRESULT CALLBACK WindowAdds::MouseHookProc(int code, WPARAM wParam, LPARAM lParam)
{
	bool cancel = false;

	if (wParam == WM_MOUSEMOVE)
	{	
		mouseHookActive = true;
	}
	else if (wParam == WM_LBUTTONUP)
	{
		HWND hWnd = Utilities::TopLevelWindowFromMousePosition();

		TITLEBARINFO tti;
		tti.cbSize = sizeof(TITLEBARINFO);
		GetTitleBarInfo(hWnd, &tti);

		MSLLHOOKSTRUCT* msllhs = (MSLLHOOKSTRUCT*)lParam;

		if ((GetKeyState(VK_CONTROL) & 0x8000) == 0x8000
			&& Utilities::RectContainsPoint(tti.rcTitleBar, msllhs->pt))
		{
			if (Utilities::IsTopMost(hWnd))
				Utilities::SetNoTopMost(hWnd);
			else
				Utilities::SetTopMost(hWnd);
		}
	}
	else if (wParam == WM_MBUTTONUP)
	{
		HWND hWnd = Utilities::TopLevelWindowFromMousePosition();

		TITLEBARINFO tti;
		tti.cbSize = sizeof(TITLEBARINFO);
		GetTitleBarInfo(hWnd, &tti);

		MSLLHOOKSTRUCT* msllhs = (MSLLHOOKSTRUCT*)lParam;

		if ((GetKeyState(VK_CONTROL) & 0x8000) == 0x8000
			&& Utilities::RectContainsPoint(tti.rcTitleBar, msllhs->pt))
		{
			//reducing
		}
	}

	return (cancel)?1L : CallNextHookEx(hMouseHook, code, wParam, lParam);
}
